package cn.tedu.charging.order.mqtt;

import cn.tedu.charging.common.pojo.JsonResult;
import cn.tedu.charging.common.pojo.param.ChargingProcessParam;
import cn.tedu.charging.common.pojo.param.GunStatusUpdateParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.common.utils.JsonUtils;
import cn.tedu.charging.order.common.ChargingConstants;
import cn.tedu.charging.order.common.GunStatusConstants;
import cn.tedu.charging.order.common.MqttConstants;
import cn.tedu.charging.order.common.OrderStatusConstants;
import cn.tedu.charging.order.pojo.dto.ChargingProcessDto;
import cn.tedu.charging.order.pojo.dto.ChargingResultDto;
import cn.tedu.charging.order.pojo.po.ChargingOrderFailPO;
import cn.tedu.charging.order.pojo.po.ChargingOrderSuccessPO;
import cn.tedu.charging.order.pojo.po.ChargingProcessPO;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.BeanUtils;

/**
 * 设备发送消息的 接受者 接受设备的发送的充电结果消息
 */

@Slf4j
public class MqttConsumer  implements MqttCallbackExtended {
    private  MqttContext mqttContext;
    private MqttClient mqttClient;
    /**
     * 没有在容器中的 MqttConsumer 想使用在容器中的MqttClient
     * 可以在创建 MqttConsumer 的 通过构造方法传入
     * 也可以 创建MqttConsumer后，通过Setter setMqttClient 写方法传入
     * @param mqttClient
     */
    public MqttConsumer(MqttClient mqttClient,MqttContext mqttContext) {
        this.mqttClient = mqttClient;
        this.mqttContext=mqttContext;
    }

    /**
     * ，通过Setter setMqttClient 写方法传入
     * @param mqttClient
     */
    public void setMqttClient(MqttClient mqttClient) {
        this.mqttClient = mqttClient;
    }





    @Override
    public void connectComplete(boolean reconnect, String serverURI) {
        log.debug("connectComplete 连接成功:reconnect:{},serverURI:{}", reconnect, serverURI);
        //订阅topic
//        设备返回的开始充电响应结果
        String topicChargingResult = MqttConstants.TOPIC_CHARGING_RESULT;

//        设备返回充电进度
        String topicChargingProcess = MqttConstants.TOPIC_CHARGING_PROCESS;

        log.debug("订阅topic:{}", topicChargingResult);
        try {
            log.debug("订阅设备返回的开始充电响应结果topic:{}", topicChargingResult);
            mqttClient.subscribe(topicChargingResult);
            log.debug("订阅设备返回的开始充电响应结果topic:{},成功", topicChargingResult);

//            订阅充电进度

            log.debug("订阅设备返回的充电进度结果topic:{}", topicChargingProcess);
            mqttClient.subscribe(topicChargingProcess);
            log.debug("订阅设备返回的开始充电进度结果topic:{},成功", topicChargingProcess);


        } catch (MqttException e) {
            //打印异常信息，有可能需要进行重连，如果重连失败，抛异常，监控系统 告警
            throw new RuntimeException(e);
        }


    }

    @Override
    public void connectionLost(Throwable cause) {
        log.debug("connectionLost 连接断开:{}", cause);

    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        log.debug("messageArrived 收到消息:topic:{},message:{}", topic, message);
        if (topic.equals(MqttConstants.TOPIC_CHARGING_RESULT)) {
            log.debug("收到设备返回的开始充电响应结果消息");
            handleChargingResult(topic, message);
        } else if (topic.equals(MqttConstants.TOPIC_CHARGING_PROCESS)) {

            log.debug("收到设备返回的充电进度消息");
            handleChargingProcess(topic, message);
        }

    }

    private void handleChargingProcess(String topic, MqttMessage message) {
        //定义消息
        ChargingProcessDto chargingProcessDto= null;
        //把设备发送的json转换为java对象 ChargingResultDto

        try {
            chargingProcessDto = JsonUtils.fromJson(message.toString(), ChargingProcessDto.class);
        } catch (Exception e) {
            log.error("设备发送的消息，无法转换为java对象 ChargingProcessDto,topic:{},消息:{}", topic, message, e);
        }
        log.debug("设备发送的消息，转换为java对象 ChargingProcessDto:{}", chargingProcessDto);
        if (chargingProcessDto != null) {
//            1.调用计价服务计算价格
           ChargingProcessVO chargingProcessVO = getCost(chargingProcessDto);
//           2，保存订单数据  ES
            saveChargingProcess(chargingProcessDto,chargingProcessVO);
            //3 返回给用户 通知用户
            sendChargingProcess2User(chargingProcessVO);



            //4 判断用户余额是否足够
            //   4.1 订单服务通过计价服务计算订单价格
            //   4.2 订单服务调用用户服务 通过用户id获取用户的余额 userClient 通过open-feign
            //   4.3 判断用户余额是否足够 用户余额 是否 大于 当前的订单价格
            //   4.4 如果大于 继续充电
            //   4.5 如果小于 提醒用户，充值，停止充电 给设备发送消息 停止充电
            //5 判断是否充满
            //   5.1 订单服务收到设备同步的充电进度数据
            //   5.2 判断是否充满
            //   5.3 如果充满
            //       通知用户 推送用户的账单 花了多少钱，充了多少度电，用了多长时间，余额还有多少
            //       修改订单状态为 已经完成
            //       扣除用户余额
            //      修改枪的状态为空
            //      可以扩张 给用户增加积分，发送优惠券
            //      无需通知设备停止充电，充满后设备会自己停止充电
            //  5.4 没充满继续
            //6 判断设备温度
            //  6.1 订单服务收到设备同步的温度数据
            //  6.2 判断温度是否正常 分级 20度-30度 大于30度 可以继续充 让用户选择 如果大于50 强制停止充电
            //  6.3 如果不正常，通知用户，停止充电，给设备发送停止充电的消息
            //  6.4 如果正常，继续充电
        }


    }

    private ChargingProcessVO getCost(ChargingProcessDto chargingProcessDto) {
        ChargingProcessParam chargingProcessParam = new ChargingProcessParam();
//        chargingProcessParam.setOrderNo(chargingProcessDto.getOrderNo());
//        chargingProcessParam.setGunId(chargingProcessDto.getGunId());
        BeanUtils.copyProperties(chargingProcessDto,chargingProcessParam);
        log.debug("调用计价服务计算价格入参：{}", chargingProcessParam);
        JsonResult<ChargingProcessVO> chargingProcessVOJsonResult = mqttContext.getCostClient().calculateCost(chargingProcessParam);
        log.debug("调用计价服务计算价格入参:{},结果:{}", chargingProcessParam,
                chargingProcessVOJsonResult);
        if (chargingProcessVOJsonResult !=null){
            return  chargingProcessVOJsonResult.getData();
        }
        return null;
    }



    private void saveChargingProcess(ChargingProcessDto chargingProcessDto,ChargingProcessVO chargingProcessVO){
        ChargingProcessPO chargingProcessPO=new ChargingProcessPO();
        //设置ID 充电数据同步的时间
        chargingProcessPO.setId(System.currentTimeMillis());
        BeanUtils.copyProperties(chargingProcessDto,chargingProcessPO);
        BeanUtils.copyProperties(chargingProcessVO,chargingProcessPO);
        mqttContext.getChargingProcessESRepository().save(chargingProcessPO);
        log.debug("保存订单数据到ES成功{}",chargingProcessPO);

    }


    private void handleChargingResult(String topic, MqttMessage message) {
        //定义消息
        ChargingResultDto chargingResultDto = null;
        //把设备发送的json转换为java对象 ChargingResultDto

        try {
            chargingResultDto = JsonUtils.fromJson(message.toString(), ChargingResultDto.class);
        } catch (Exception e) {
            log.error("设备发送的消息，无法转换为java对象 ChargingResultDto,topic:{},消息:{}", topic, message, e);
        }
        log.debug("设备发送的消息，转换为java对象 ChargingResultDto:{}", chargingResultDto);
        if (chargingResultDto != null){
            String result=chargingResultDto.getResult();
            if (ChargingConstants.CHARGING_RESULT_SUCCESS.equals(result)){
                log.debug("设备发送的消息，是充电成功的消息");
                ChargingOrderSuccessPO chargingOrderSuccessPO = new ChargingOrderSuccessPO();
                chargingOrderSuccessPO.setBillId(chargingResultDto.getOrderNo());
                chargingOrderSuccessPO.setGunId(chargingResultDto.getGunId());
                chargingOrderSuccessPO.setBillStatus(OrderStatusConstants.ORDER_STATUS_PROCESS);
                log.debug("保存订单成功信息:{}",chargingOrderSuccessPO);
                Integer row=mqttContext.getOrderSuccessMapper().insert(chargingOrderSuccessPO);
                log.debug("保存订单成功信息{}，受影响行数:{}",chargingOrderSuccessPO,row);
            }else{
                log.debug("设备发送的消息，是充电失败的消息");
                ChargingOrderFailPO chargingOrderFailPO = new ChargingOrderFailPO();
                chargingOrderFailPO.setBillId(chargingResultDto.getOrderNo());
                chargingOrderFailPO.setFailDesc("设备问题，开始充电失败");
                log.debug("保存订单失败信息:{}",chargingOrderFailPO);
                Integer row=mqttContext.getOrderFaileMapper().insert(chargingOrderFailPO);
                log.debug("保存订单失败信息{}，受影响行数:{}",chargingOrderFailPO,row);
                log.debug("设备发送的消息，是充电失败的消息，通知用户");
                log.debug("给用户发送优惠券 表示歉意 给用户心灵上的安慰");
                log.debug("通知设备管理员 进行设备的检修");
                log.debug("修改枪的状态 为 故障");
                GunStatusUpdateParam gunStatusUpdateParam=new GunStatusUpdateParam();
                gunStatusUpdateParam.setGunId(chargingResultDto.getGunId());
                gunStatusUpdateParam.setStatus(GunStatusConstants.GUN_STATUS_FAIL);
                mqttContext.getDeviceClient().updateGunStatus(gunStatusUpdateParam);
                log.debug("修改枪的状态 为 故障，gunStatusUpdateParam:{}",gunStatusUpdateParam);


            }
        }
    }

    /**
     * 给用户发送充电进度
     * @param chargingProcessVO
     */
    private void sendChargingProcess2User(ChargingProcessVO chargingProcessVO){
        //把chargingProcessVO转换为json JsonUtils
        String json = JsonUtils.toJson(chargingProcessVO);
        log.debug("同步充电数据,用户:{},消息:{}",chargingProcessVO.getUserId(),json);
        mqttContext.getWebSocketServer().sendMessage(chargingProcessVO.getUserId(),json);
        log.debug("同步充电数据,用户:{},消息:{}",chargingProcessVO.getUserId(),json);



    }



    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        log.debug("消息发送完成");


    }
}
